
package com.example.paper.testCache;


import com.example.paper.common.entity.RuleB;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Guava Cache缓存测试类
 */
@Slf4j
public class TestGuavaCache {

    /**
     * Guava Cache 测试用例
     * 测试Guava Cache调用机制，调用同一个key，只要命中缓存，之后的调用都是从缓存中获取
     * 单双层key设计对比
     * @param args
     */
    public static void main(String[] args) throws ExecutionException {

        LocalDateTime dateOfIssue = LocalDateTime.of(2017, 9, 20, 0, 0);

        // 第一次调用 a-b 会从数据库中获取数据，会打印调用日志，之后的调用都是从缓存中获取
        List<RuleB> a_bRuleBList1 = cache1.get("a-b");
        log.info("a_bRuleBList1:条数{},具体信息:{}", a_bRuleBList1.size(), a_bRuleBList1);
        // 第二次调用 a-b 会从缓存中获取数据
        List<RuleB> a_bRuleBList2 = cache1.get("a-b");
        log.info("a_bRuleBList2:条数{},具体信息:{}", a_bRuleBList2.size(), a_bRuleBList2);
        // 获取cache2中key为a-b,且dateOfIssue在beginTime和endTime之间的数据
        List<Map.Entry<Pair<LocalDateTime, LocalDateTime>, List<RuleB>>> a_bRuleBList3 =
                cache2.get("a-b").entrySet().stream().filter(o -> o.getKey().getLeft().isBefore(dateOfIssue)
                        && o.getKey().getRight().isAfter(dateOfIssue)).collect(Collectors.toList());
        log.info("a_bRuleBList3:条数{},具体信息:{}", a_bRuleBList3.size(), a_bRuleBList3);
    }

    private static final RuleB ruleB1 = new RuleB().setName("ruleB1").setRouteDetail("a-b")
            .setBeginTime(LocalDateTime.of(2017, 8, 31, 0, 0))
            .setEndTime(LocalDateTime.of(2017, 9, 30, 0, 0));

    private static final RuleB ruleB2 = new RuleB().setName("ruleB2").setRouteDetail("a-b")
            .setBeginTime(LocalDateTime.of(2017, 8, 30, 0, 0))
            .setEndTime(LocalDateTime.of(2017, 9, 10, 0, 0));

    private static final RuleB ruleB3 = new RuleB().setName("ruleB3").setRouteDetail("c-d")
            .setBeginTime(LocalDateTime.of(2018, 9, 1, 0, 0))
            .setEndTime(LocalDateTime.of(2018, 9, 10, 0, 0));

    private static final List<RuleB> ruleBList = Stream.of(ruleB1, ruleB2, ruleB3).collect(Collectors.toList());

    // 单层key设计，以routDetail字段作为key，value为List<RuleB>
    private static final LoadingCache<String, List<RuleB>> cache1 = CacheBuilder.newBuilder()
            // 设置缓存最大容量为1000 默认LRU驱逐策略
            .maximumSize(1000)
            .build(CacheLoader.from((routDetail)->{
                System.out.println("cache1查询数据库");
                sleep1s(); // 模拟从数据库中获取数据
                return ruleBList.stream().filter(ruleB -> ruleB.getRouteDetail().equals(routDetail)).collect(Collectors.toList());
            }));

    // 多层key设计，以routDetail字段作为第一层的key，beginTime和endTime作为第二层的key，value为List<RuleB>
    private static final LoadingCache<String, Map<Pair<LocalDateTime, LocalDateTime>, List<RuleB>>> cache2 = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .build(CacheLoader.from((routDetail)->{
                System.out.println("cache2查询数据库");
                sleep1s(); // 模拟从数据库中获取数据
                Map<Pair<LocalDateTime, LocalDateTime>, List<RuleB>> map = ruleBList.stream().filter(ruleB -> ruleB.getRouteDetail()
                        .equals(routDetail)).collect(Collectors.groupingBy(o -> Pair.of(o.getBeginTime(), o.getEndTime()),
                        Collectors.mapping(o -> o, Collectors.toList())));
                return map;
            }));

    /**
     *  模拟从数据库中获取数据
     */
    private static void sleep1s() {
        try {

            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
